'use strict';

/**
 * @ngdoc service
 * @name faApp.cookies
 * @description
 * # cookies
 * Factory in the faApp.
 */
angular.module('starter.cookie',[])
  .factory('$faCookies', function($document) {

    var Cookies = function(key, value, options) {
      return arguments.length === 1 ? Cookies.get(key) : Cookies.set(key, value, options);
    };

    // Allows for setter injection in unit tests
    Cookies._document = $document[0];

    // Used to ensure cookie keys do not collide with
    // built-in `Object` properties
    Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :)

    Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC');

    Cookies.defaults = {
      path: '/',
      secure: false
    };

    Cookies.get = function(key) {

      var _val;

      if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
        Cookies._renewCache();
      }

      _val = Cookies._cache[Cookies._cacheKeyPrefix + key];

      return _val && _val.indexOf('{') !== -1 ? angular.fromJson(_val) : _val;
    };

    Cookies.set = function(key, value, options) {
      options = Cookies._getExtendedOptions(options);
      options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);

      value = value && typeof value === 'object' ? angular.toJson(value) : value;

      Cookies._document.cookie = Cookies._generateCookieString(key, value, options);

      return Cookies;
    };

    Cookies.expire = function(key, options) {
      return Cookies.set(key, undefined, options);
    };

    Cookies._getExtendedOptions = function(options) {
      return {
        path: options && options.path || Cookies.defaults.path,
        domain: options && options.domain || Cookies.defaults.domain,
        expires: options && options.expires || Cookies.defaults.expires,
        secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure
      };
    };

    Cookies._isValidDate = function(date) {
      return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
    };

    Cookies._getExpiresDate = function(expires, now) {
      now = now || new Date();

      if (typeof expires === 'number') {
        expires = expires === Infinity ?
          Cookies._maxExpireDate : new Date(now.getTime() + expires * 1000);
      } else if (typeof expires === 'string') {
        expires = new Date(expires);
      }

      if (expires && !Cookies._isValidDate(expires)) {
        throw new Error('`expires` parameter cannot be converted to a valid Date instance');
      }

      return expires;
    };

    Cookies._generateCookieString = function(key, value, options) {
      key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
      key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
      value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
      options = options || {};

      var cookieString = key + '=' + value;
      cookieString += options.path ? ';path=' + options.path : '';
      cookieString += options.domain ? ';domain=' + options.domain : '';
      cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
      cookieString += options.secure ? ';secure' : '';

      return cookieString;
    };

    Cookies._getCacheFromString = function(documentCookie) {
      var cookieCache = {};
      var cookiesArray = documentCookie ? documentCookie.split('; ') : [];

      for (var i = 0; i < cookiesArray.length; i++) {
        var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);

        if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) {
          cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value;
        }
      }

      return cookieCache;
    };

    Cookies._getKeyValuePairFromCookieString = function(cookieString) {
      // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
      var separatorIndex = cookieString.indexOf('=');

      // IE omits the "=" when the cookie value is an empty string
      separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;

      return {
        key: decodeURIComponent(cookieString.substr(0, separatorIndex)),
        value: decodeURIComponent(cookieString.substr(separatorIndex + 1))
      };
    };

    Cookies._renewCache = function() {
      Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
      Cookies._cachedDocumentCookie = Cookies._document.cookie;
    };

    Cookies._areEnabled = function() {
      var testKey = 'cookies.js';
      var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
      Cookies.expire(testKey);
      return areEnabled;
    };

    Cookies.enabled = Cookies._areEnabled();

    return Cookies;
  });
